#include <fcntl.h>
#include <string.h>
#include "assert_helper.h"
#include "partition_helper.h"

const char * STRING_FAT = "vfat";
const char * STRING_NTFS = "ntfs";
const char * STRING_EXT2 = "ext2";
const char * STRING_EXT3 = "ext3";
const char * STRING_EXT4 = "ext4";

Partition_Information::Partition_Information() :
drive_(0), type_(UNKNOW), size_(0)
{
}

bool
Partition_Information:: operator == (Partition_Information & info)
{
    if (this == &info) return true;
    return (name_ == info.name_ && size_ == info.size_) ? true : false;
}

Partition_Information::Type
Partition_Information::parse_string_for_type(const char *type_str)
{
	std::string str(type_str);
	if (str == STRING_FAT || str == "fat" || str == "fat32" || str == "fat16") {
		return FAT;
	} else if (str == STRING_NTFS) {
		return NTFS;
	} else if (str == STRING_EXT2) {
		return EXT2;
	} else if (str == STRING_EXT3) {
		return EXT3;
	} else if (str == STRING_EXT4) {
		return EXT4;
	} else {
		return UNKNOW;
	}
}

const char *
Partition_Information::parse_type_for_string(Type type)
{
	switch (type) {
	case FAT:
		return "fat";
	case NTFS:
		return STRING_NTFS;
	case EXT2:
		return STRING_EXT2;
	case EXT3:
		return STRING_EXT3;
	case EXT4:
		return STRING_EXT4;
	case UNKNOW:
		return "";
	default:
		break;
	}
	return "";
}

Partition_Helper::Partition_Helper()
{
	probe_for_windows_partition();	// 收集Windows分区
	partition_information_analyze();// 分析分区具体信息
}

Partition_Helper::~Partition_Helper()
{
	Partitions::iterator begin = partitions_.begin();
	Partitions::iterator end = partitions_.end();
	for (; begin != end; ++begin) {
		delete *begin;
	}
	partitions_.clear();
}

int Partition_Helper::probe_for_windows_partition()
{
    blkid_dev_iterate   iter = NULL;
    blkid_dev   dev = NULL;
    blkid_cache cache = NULL;
    Partition_Information * partition_info = NULL;
    int total = 0;

    if (blkid_get_cache(&cache, 0) < 0) return -1;
    blkid_probe_all(cache); // probe all partitions
    iter = blkid_dev_iterate_begin(cache);
    blkid_dev_set_search(iter, NULL, NULL);
    while (blkid_dev_next(iter, &dev) == 0) {
        dev = blkid_verify(cache, dev);
        if (!dev) continue;
        if (is_windows_partition(dev)) {
        	partition_info = get_partition_parameters(dev);
        	if (NULL != partition_info) {
        		partitions_.push_back(partition_info);
        		++total;
        	}
        }
    }
    blkid_dev_iterate_end(iter);
    return total;
}

bool    Partition_Helper::is_windows_partition(blkid_dev dev)
{
	IF_NULL_RETURN_VALUE(dev, NULL);
	blkid_tag_iterate	iter = NULL;
	const char			*type = NULL, *value = NULL;
	bool	is_windows_partition = false;

	iter = blkid_tag_iterate_begin(dev);
	while (blkid_tag_next(iter, &type, &value) == 0) {
		if (0 == strcmp(type, "TYPE") &&
			(0 == strcmp(value, STRING_NTFS) ||
			0 == strcmp(value, STRING_FAT))) {
			is_windows_partition = true;	// NTFS 或 FAT 分区被视为Windows分区
			break;
		}
	}
	blkid_tag_iterate_end(iter);
    return is_windows_partition;
}

size_t Partition_Helper::get_partition_total() const
{
	return partitions_.size();
}

// 获取分区大小，需要root权限
blkid_loff_t Partition_Helper::get_partition_size(blkid_dev dev)
{
	IF_NULL_RETURN_VALUE(dev, NULL);
	blkid_loff_t size = 0ULL;

    int fd = open(blkid_dev_devname(dev), O_RDONLY);
    if (-1 != fd)   {
    	size = blkid_get_dev_size(fd);
        close(fd);
    }
    return size;
}

Partition_Information
Partition_Helper::get_partition_information_at(size_t index) const
{
	if (index >= partitions_.size()) return Partition_Information();
	return *(partitions_.at(index));
}

Partition_Information *
Partition_Helper::get_partition_parameters(blkid_dev dev)
{
    IF_NULL_RETURN_VALUE(dev, NULL);
    Partition_Information * result = new Partition_Information;
    if (NULL != result) {
    	blkid_tag_iterate	iter = NULL;
    	const char			*type = NULL, *value = NULL;
    	bool	is_first = true;

    	iter = blkid_tag_iterate_begin(dev);
    	while (blkid_tag_next(iter, &type, &value) == 0) {
    		if (is_first) {
    			is_first = false;
    			result->name_ = blkid_dev_devname(dev);
    		}
    		if (0 == strcmp(type, "UUID")) {
    			result->uuid_string = value;
    		} else if (0 == strcmp(type, "LABEL")) {
				result->label_ = value;
    		} else if (0 == strcmp(type, "TYPE")) {
    			result->type_ = Partition_Information::parse_string_for_type(value);
    		}
    	}
    	blkid_tag_iterate_end(iter);
    	result->size_ = get_partition_size(dev);
    }

    return result;
}

void Partition_Helper::partition_information_analyze()
{
}








